/*  
 *  Copyright (c) 2008 Adrian Necimal <necimal@gmail.com>
 *  
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *  
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;

Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://app/jsmodules/sbProperties.jsm");
Cu.import("resource://app/jsmodules/sbLibraryUtils.jsm");

function SongbirdRecommendations() {
  this.wrappedJSObject = this;
};

SongbirdRecommendations.prototype = {
  classDescription: "Songbird Recommendations Javascript XPCOM Component",
  classID: Components.ID("{DB48C84A-0A92-40e3-8ACC-C21CC6D2D873}"),
  contractID: "@necimal.com/recommendations;1",
  QueryInterface: XPCOMUtils.generateQI([])
}

/*
 * Get some xul to display information about an artist.
 */
SongbirdRecommendations.prototype.getArtistDOM = function(artist) {
  // check the last artist queried cache
  if (this._recentArtist == artist) {
    return this._recentArtistDOM;
  }
  
  // retrieve the artist info from last.fm
  var artistInfo = this.getArtistInfo(artist);
  if (!artistInfo) { return null; }
  
  // localisation
  var sbService = Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci.nsIStringBundleService);
  var strings = sbService.createBundle("chrome://music-recommendations/locale/recommendations.properties");
  
  // create a new xul document to branch from
  var doc = Cc["@mozilla.org/xul/xul-document;1"].createInstance(Ci.nsIDOMDocument);
  
  // generate the xul
  var eRoot = doc.createElement("vbox");
  eRoot.setAttribute("id", "music-recommendations-list");
  
  var eArtistTitleBox = doc.createElement("hbox");
  eArtistTitleBox.setAttribute("class", "artist-title-box");
  eArtistTitleBox.setAttribute("onclick", "MusicRecommendations.openUrl('" + artistInfo.url + "');");
  eRoot.appendChild(eArtistTitleBox);
  
  var eArtistName = doc.createElement("description");
  eArtistName.setAttribute("class", "artist-name");
  eArtistName.appendChild(doc.createTextNode(artistInfo.name));
  eArtistTitleBox.appendChild(eArtistName);
  
  var eArtistImg = doc.createElement("image");
  eArtistImg.setAttribute("class", "artist-img");
  eArtistImg.setAttribute("src", artistInfo.image);
  eArtistTitleBox.appendChild(eArtistImg);
  
  var eArtistDesc = doc.createElement("description");
  eArtistDesc.setAttribute("class", "artist-description");
  eArtistDesc.appendChild(doc.createTextNode(artistInfo.summary));
  eRoot.appendChild(eArtistDesc);
  
  var eSimArtsTitle = doc.createElement("description");
  eSimArtsTitle.setAttribute("class", "similar-artists-title");
  eSimArtsTitle.setAttribute("value", strings.GetStringFromName("music-recommendations.similarartists"));
  eRoot.appendChild(eSimArtsTitle);
  
  var eSimArtsBox = doc.createElement("vbox");
  eSimArtsBox.setAttribute("class", "similar-artists-box");
  eRoot.appendChild(eSimArtsBox);
  
  // for each similar artist
  for (i=0; i < artistInfo.similar.length; i++) {
    // generate some more xul
    var eSimArt = doc.createElement("hbox");
    eSimArt.setAttribute("class", "similar-artist");
    eSimArt.setAttribute("onclick", "MusicRecommendations.openUrl('" + artistInfo.similar[i].url + "');");
    eSimArtsBox.appendChild(eSimArt);
    
    var eSimArtImg = doc.createElement("image");
    eSimArtImg.setAttribute("src", artistInfo.similar[i].image);
    eSimArt.appendChild(eSimArtImg);
    
    var eSimArtTextBox = doc.createElement("vbox");
    eSimArtTextBox.setAttribute("class", "similar-artist-textbox");
    eSimArt.appendChild(eSimArtTextBox);
    
    var eSimArtName = doc.createElement("description");
    eSimArtName.setAttribute("value", artistInfo.similar[i].name);
    eSimArtTextBox.appendChild(eSimArtName);
    
    var eSimArtNumTracks = doc.createElement("description");
    if (artistInfo.similar[i].count == 1) {
      eSimArtNumTracks.setAttribute("value", strings.GetStringFromName("music-recommendations.onetrack"));
    }
    else {
    eSimArtNumTracks.setAttribute("value", strings.formatStringFromName("music-recommendations.numtracks",
                                                                        [artistInfo.similar[i].count], 1));
    }
    eSimArtTextBox.appendChild(eSimArtNumTracks);
  }
  
  // cache the artist's details
  this._recentArtist = artist;
  this._recentArtistDOM = eRoot;
  
  return eRoot;
}
  
/*
 * Convert info from Last.fm into a simple JS object.
 */
SongbirdRecommendations.prototype.getArtistInfo = function(artist) {
  var result = { };
  result.queryUrl = "http://ws.audioscrobbler.com/2.0/artist/info.xml?artist=" + this.escapeUrl(artist);
  
  // make a request to Last.fm
  var req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest);
  
  req.open("GET", result.queryUrl, false);
  req.send(null);
  if (!req.responseXML) {
    return null;
  }
  
  // get some basic details
  var artistNode = req.responseXML.getElementsByTagName("artist").item(0);
  result.name = artistNode.getElementsByTagName("name").item(0).firstChild.nodeValue;
  result.url = artistNode.getElementsByTagName("url").item(0).firstChild.nodeValue;
  
  var imageNode = artistNode.getElementsByTagName("image").item(1);
  if (imageNode.firstChild) {
    result.image = imageNode.firstChild.nodeValue;
  } else { result.image = ""; }
  
  // get a bio
  var summaryNode = artistNode.getElementsByTagName("summary").item(0);
  if (summaryNode.firstChild) {
    result.summary = this.stripHtml(summaryNode.firstChild.nodeValue);
  } else { result.summary = ""; }
  
  // get number of tracks
  var artistCounter = new EnumerationCountListener();
  LibraryUtils.mainLibrary.enumerateItemsByProperty(SBProperties.artistName, result.name, artistCounter);
  result.count = artistCounter.count;
  
  // get similar artists
  var similarNode = artistNode.getElementsByTagName("similar").item(0);
  var similarArtistNodes = similarNode.getElementsByTagName("artist");
  result.similar = new Array(similarArtistNodes.length);
  
  for (i=0; i < similarArtistNodes.length; i++) {
    var similarArtist = similarArtistNodes.item(i);
    
    // find some details on each similar artist
    result.similar[i] = { };
    result.similar[i].name = similarArtist.getElementsByTagName("name").item(0).firstChild.nodeValue;
    result.similar[i].url = similarArtist.getElementsByTagName("url").item(0).firstChild.nodeValue;
    
    var similarArtistImage = similarArtist.getElementsByTagName("image").item(0);
    if (similarArtistImage.firstChild) {
      result.similar[i].image = similarArtistImage.firstChild.nodeValue
    } else { result.similar[i].image = "chrome://music-recommendations/skin/no-image.png"; }
    
    // count the number of tracks we have by this artist
    var simArtistCounter = new EnumerationCountListener();
    LibraryUtils.mainLibrary.enumerateItemsByProperty(SBProperties.artistName,
                                                      result.similar[i].name,
                                                      simArtistCounter);
    result.similar[i].count = simArtistCounter.count;
  }
  
  // finished getting artist info
  return result;
}

/*
 * HTML-encode ampersands in a url.
 */
SongbirdRecommendations.prototype.escapeUrl = function(url) {
    if (!url) { return ""; }
    else {
      url = url.replace(/&/g, "%26");
      return url;
    }
}

/*
 * Strip HTML tags from a string of text.
 */
SongbirdRecommendations.prototype.stripHtml = function(html) {
    html = html.replace(/(<([^>]+)>)/ig, "");
    html = html.replace(/&quot;/ig, "\"");
    html = html.replace(/&amp;/ig, "&");
    return html;
}

/*
 * Counts the entries in an enumeration (used for counting tracks by an artist).
 */
function EnumerationCountListener() { }
EnumerationCountListener.prototype = {
  count: 0,
  onEnumerationBegin: function(aMediaList) {
    this.count = 0;
  },
  onEnumeratedItem: function(aMediaList, aMediaItem) {
    this.count++;
  },
  onEnumerationEnd: function(aMediaList, aStatusCode) { }
}

var components = [SongbirdRecommendations];
function NSGetModule(compMgr, fileSpec) {
  return XPCOMUtils.generateModule(components);
}
